﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.Text;
using System.Drawing;

namespace DotNetGPSTest
{
    class MapRenderer
    {
        int m_tw; // tiles in width
        int m_tit; // tiles in total
        Tile[] m_tiles;
        Tile[] m_cache;

        public int m_centreEast = 0;
        public int m_centreNorth = 0;
        public int m_viewWidth = 240;
        public int m_viewHeight = 180;

        MapSpec m_spec;

        public MapRenderer(MapSpec spec, int viewWidth, int viewHeight, int tileWidth)
        {
            m_viewWidth = viewWidth;
            m_viewHeight = viewHeight;
            m_spec = spec;
            m_tw = tileWidth;
            m_tit = m_tw * m_tw; // tiles in total
            m_tiles = new Tile[m_tit];
            m_cache = new Tile[m_tit];
        }

        public void setMapSpec(MapSpec spec)
        {
           m_spec = spec;
           // Update cache
           for (int i = 0; i < m_tit; i++)
           {
              //m_tiles[i] = null;
              //m_cache[i] = null;
           }
        }

        public double mpp()
        {
            return m_spec.mpp();
        }

        public void drawTilesAt(int e, int n, IRendererListener listener)
        {
            m_centreEast = e;
            m_centreNorth = n;

            loadMapArrayAt(e, n);
            listener.draw();
        }

        public void drawTiles(Graphics g)
        {
            // TODO sometime use m_tit
            if (m_tw == 3) // HP
            {
                if (m_tiles[0] != null) drawMapTile(m_centreEast - m_spec.m_tileWidthMetres, m_centreNorth + m_spec.m_tileWidthMetres, g, m_tiles[0]);
                if (m_tiles[1] != null) drawMapTile(m_centreEast, m_centreNorth + m_spec.m_tileWidthMetres, g, m_tiles[1]);
                if (m_tiles[2] != null) drawMapTile(m_centreEast + m_spec.m_tileWidthMetres, m_centreNorth + m_spec.m_tileWidthMetres, g, m_tiles[2]);
                if (m_tiles[3] != null) drawMapTile(m_centreEast - m_spec.m_tileWidthMetres, m_centreNorth, g, m_tiles[3]);
                if (m_tiles[4] != null) drawMapTile(m_centreEast, m_centreNorth, g, m_tiles[4]);
                if (m_tiles[5] != null) drawMapTile(m_centreEast + m_spec.m_tileWidthMetres, m_centreNorth, g, m_tiles[5]);
                if (m_tiles[6] != null) drawMapTile(m_centreEast - m_spec.m_tileWidthMetres, m_centreNorth - m_spec.m_tileWidthMetres, g, m_tiles[6]);
                if (m_tiles[7] != null) drawMapTile(m_centreEast, m_centreNorth - m_spec.m_tileWidthMetres, g, m_tiles[7]);
                if (m_tiles[8] != null) drawMapTile(m_centreEast + m_spec.m_tileWidthMetres, m_centreNorth - m_spec.m_tileWidthMetres, g, m_tiles[8]);
            }
            else // Loox
            {
                if (m_tiles[0] != null) drawMapTile(m_centreEast - (int)((float)m_spec.m_tileWidthMetres*1.5), m_centreNorth + (int)((float)m_spec.m_tileWidthMetres*1.5), g, m_tiles[0]);
                if (m_tiles[1] != null) drawMapTile(m_centreEast - (int)((float)m_spec.m_tileWidthMetres*0.5), m_centreNorth + (int)((float)m_spec.m_tileWidthMetres*1.5), g, m_tiles[1]);
                if (m_tiles[2] != null) drawMapTile(m_centreEast + (int)((float)m_spec.m_tileWidthMetres*0.5), m_centreNorth + (int)((float)m_spec.m_tileWidthMetres*1.5), g, m_tiles[2]);
                if (m_tiles[3] != null) drawMapTile(m_centreEast + (int)((float)m_spec.m_tileWidthMetres*1.5), m_centreNorth + (int)((float)m_spec.m_tileWidthMetres*1.5), g, m_tiles[3]);
                if (m_tiles[4] != null) drawMapTile(m_centreEast - (int)((float)m_spec.m_tileWidthMetres*1.5), m_centreNorth + (int)((float)m_spec.m_tileWidthMetres*0.5), g, m_tiles[4]);
                if (m_tiles[5] != null) drawMapTile(m_centreEast - (int)((float)m_spec.m_tileWidthMetres * 0.5), m_centreNorth + (int)((float)m_spec.m_tileWidthMetres * 0.5), g, m_tiles[5]);
                if (m_tiles[6] != null) drawMapTile(m_centreEast + (int)((float)m_spec.m_tileWidthMetres * 0.5), m_centreNorth + (int)((float)m_spec.m_tileWidthMetres * 0.5), g, m_tiles[6]);
                if (m_tiles[7] != null) drawMapTile(m_centreEast + (int)((float)m_spec.m_tileWidthMetres * 1.5), m_centreNorth + (int)((float)m_spec.m_tileWidthMetres * 0.5), g, m_tiles[7]);
                if (m_tiles[8] != null) drawMapTile(m_centreEast - (int)((float)m_spec.m_tileWidthMetres * 1.5), m_centreNorth - (int)((float)m_spec.m_tileWidthMetres * 0.5), g, m_tiles[8]);
                if (m_tiles[9] != null) drawMapTile(m_centreEast - (int)((float)m_spec.m_tileWidthMetres * 0.5), m_centreNorth - (int)((float)m_spec.m_tileWidthMetres * 0.5), g, m_tiles[9]);
                if (m_tiles[10] != null) drawMapTile(m_centreEast + (int)((float)m_spec.m_tileWidthMetres * 0.5), m_centreNorth - (int)((float)m_spec.m_tileWidthMetres * 0.5), g, m_tiles[10]);
                if (m_tiles[11] != null) drawMapTile(m_centreEast + (int)((float)m_spec.m_tileWidthMetres * 1.5), m_centreNorth - (int)((float)m_spec.m_tileWidthMetres * 0.5), g, m_tiles[11]);
                if (m_tiles[12] != null) drawMapTile(m_centreEast - (int)((float)m_spec.m_tileWidthMetres * 1.5), m_centreNorth - (int)((float)m_spec.m_tileWidthMetres * 1.5), g, m_tiles[12]);
                if (m_tiles[13] != null) drawMapTile(m_centreEast - (int)((float)m_spec.m_tileWidthMetres * 0.5), m_centreNorth - (int)((float)m_spec.m_tileWidthMetres * 1.5), g, m_tiles[13]);
                if (m_tiles[14] != null) drawMapTile(m_centreEast + (int)((float)m_spec.m_tileWidthMetres * 0.5), m_centreNorth - (int)((float)m_spec.m_tileWidthMetres * 1.5), g, m_tiles[14]);
                if (m_tiles[15] != null) drawMapTile(m_centreEast + (int)((float)m_spec.m_tileWidthMetres * 1.5), m_centreNorth - (int)((float)m_spec.m_tileWidthMetres * 1.5), g, m_tiles[15]);
            }

        }

        private void drawMapTile(int e, int n, Graphics g, Tile b)
        {
            int botLeftTileE = (e / m_spec.m_tileWidthMetres) * m_spec.m_tileWidthMetres;
            int botLeftTileN = (n / m_spec.m_tileWidthMetres) * m_spec.m_tileWidthMetres;

            double dE = m_centreEast - botLeftTileE;
            double dN = m_centreNorth - botLeftTileN;
            double dE2 = dE / m_spec.m_tileWidthMetres * m_spec.m_tileWidthPixels;
            double dN2 = dN / m_spec.m_tileWidthMetres * m_spec.m_tileWidthPixels;
            double fdrawX = (m_viewWidth / 2.0) - dE2;
            double fdrawY = (m_viewHeight / 2.0) + dN2;
            int drawX = (int)fdrawX;
            int drawY = (int)fdrawY - m_spec.m_tileWidthPixels;

            g.DrawImage(b.m_bmp, drawX, drawY);
            
        }

        private void loadBitmapAt(int e, int n, ref Tile bmp)
        {
            int botLeftTileE = (e / m_spec.m_tileWidthMetres) * m_spec.m_tileWidthMetres;
            int botLeftTileN = (n / m_spec.m_tileWidthMetres) * m_spec.m_tileWidthMetres;
            string easting = botLeftTileE.ToString("D7");
            string northing = botLeftTileN.ToString("D7");
            string folderE = easting.Substring(0, 2);
            string folderN = northing.Substring(0, 2);
            string folder = m_spec.m_folderPrefix + folderE + folderN;
            string file = easting.Substring(1, 4) + northing.Substring(1, 4) + m_spec.m_fileExt;
            string path = m_spec.m_mapsFolder + "\\" + folder + "\\" + file;

            bool foundInCache = false;
            for (int i = 0; i < m_tit; i++)
            {
                if (m_cache[i] != null && m_cache[i].m_path == path)
                {
                    bmp = m_cache[i]; // Use the one in cache
                    foundInCache = true;
                    break;
                }
            }
            if (!foundInCache)
            {
               
                if (bmp == null || (bmp != null && bmp.m_path != path)) // New bitmap
                {
                   try
                   {
                      bmp = new Tile(new Bitmap(path), path);
                   }
                   catch (Exception)
                   {
                      bmp = null;
                   }
                }
            }

        }

        public string getBitmapPathAt(int e, int n)
        {
            int botLeftTileE = (e / m_spec.m_tileWidthMetres) * m_spec.m_tileWidthMetres;
            int botLeftTileN = (n / m_spec.m_tileWidthMetres) * m_spec.m_tileWidthMetres;
            string easting = botLeftTileE.ToString("D7");
            string northing = botLeftTileN.ToString("D7");
            string folderE = easting.Substring(0, 2);
            string folderN = northing.Substring(0, 2);
            string folder = m_spec.m_folderPrefix + folderE + folderN;
            string file = easting.Substring(1, 4) + northing.Substring(1, 4) + m_spec.m_fileExt;
            string path = m_spec.m_mapsFolder + "\\" + folder + "\\" + file;
            return path;
        }

        private void loadMapArrayAt(int e, int n)
        {
            // TODO sometime use m_tit
            if (m_tw == 3) // HP
            {
                loadBitmapAt(e - m_spec.m_tileWidthMetres, n + m_spec.m_tileWidthMetres, ref m_tiles[0]);
                loadBitmapAt(e, n + m_spec.m_tileWidthMetres, ref m_tiles[1]);
                loadBitmapAt(e + m_spec.m_tileWidthMetres, n + m_spec.m_tileWidthMetres, ref m_tiles[2]);
                loadBitmapAt(e - m_spec.m_tileWidthMetres, n, ref m_tiles[3]);
                loadBitmapAt(e, n, ref m_tiles[4]);
                loadBitmapAt(e + m_spec.m_tileWidthMetres, n, ref m_tiles[5]);
                loadBitmapAt(e - m_spec.m_tileWidthMetres, n - m_spec.m_tileWidthMetres, ref m_tiles[6]);
                loadBitmapAt(e, n - m_spec.m_tileWidthMetres, ref m_tiles[7]);
                loadBitmapAt(e + m_spec.m_tileWidthMetres, n - m_spec.m_tileWidthMetres, ref m_tiles[8]);
            }
            else
            {
                loadBitmapAt(e - (int)((float)m_spec.m_tileWidthMetres * 1.5), n + (int)((float)m_spec.m_tileWidthMetres * 1.5), ref m_tiles[0]);
                loadBitmapAt(e - (int)((float)m_spec.m_tileWidthMetres * 0.5), n + (int)((float)m_spec.m_tileWidthMetres * 1.5), ref m_tiles[1]);
                loadBitmapAt(e + (int)((float)m_spec.m_tileWidthMetres * 0.5), n + (int)((float)m_spec.m_tileWidthMetres * 1.5), ref m_tiles[2]);
                loadBitmapAt(e + (int)((float)m_spec.m_tileWidthMetres * 1.5), n + (int)((float)m_spec.m_tileWidthMetres * 1.5), ref m_tiles[3]);
                loadBitmapAt(e - (int)((float)m_spec.m_tileWidthMetres * 1.5), n + (int)((float)m_spec.m_tileWidthMetres * 0.5), ref m_tiles[4]);
                loadBitmapAt(e - (int)((float)m_spec.m_tileWidthMetres * 0.5), n + (int)((float)m_spec.m_tileWidthMetres * 0.5), ref m_tiles[5]);
                loadBitmapAt(e + (int)((float)m_spec.m_tileWidthMetres * 0.5), n + (int)((float)m_spec.m_tileWidthMetres * 0.5), ref m_tiles[6]);
                loadBitmapAt(e + (int)((float)m_spec.m_tileWidthMetres * 1.5), n + (int)((float)m_spec.m_tileWidthMetres * 0.5), ref m_tiles[7]);
                loadBitmapAt(e - (int)((float)m_spec.m_tileWidthMetres * 1.5), n - (int)((float)m_spec.m_tileWidthMetres * 0.5), ref m_tiles[8]);
                loadBitmapAt(e - (int)((float)m_spec.m_tileWidthMetres * 0.5), n - (int)((float)m_spec.m_tileWidthMetres * 0.5), ref m_tiles[9]);
                loadBitmapAt(e + (int)((float)m_spec.m_tileWidthMetres * 0.5), n - (int)((float)m_spec.m_tileWidthMetres * 0.5), ref m_tiles[10]);
                loadBitmapAt(e + (int)((float)m_spec.m_tileWidthMetres * 1.5), n - (int)((float)m_spec.m_tileWidthMetres * 0.5), ref m_tiles[11]);
                loadBitmapAt(e - (int)((float)m_spec.m_tileWidthMetres * 1.5), n - (int)((float)m_spec.m_tileWidthMetres * 1.5), ref m_tiles[12]);
                loadBitmapAt(e - (int)((float)m_spec.m_tileWidthMetres * 0.5), n - (int)((float)m_spec.m_tileWidthMetres * 1.5), ref m_tiles[13]);
                loadBitmapAt(e + (int)((float)m_spec.m_tileWidthMetres * 0.5), n - (int)((float)m_spec.m_tileWidthMetres * 1.5), ref m_tiles[14]);
                loadBitmapAt(e + (int)((float)m_spec.m_tileWidthMetres * 1.5), n - (int)((float)m_spec.m_tileWidthMetres * 1.5), ref m_tiles[15]);
            }

            // Update cache
            for (int i = 0; i < m_tit; i++)
            {
                m_cache[i] = m_tiles[i];
            }
        }
    }

    
}
